// Ignore argument count for test functions.
#![allow(clippy::too_many_arguments)]

use std::collections;
use ndarray::{array, Array2};
use rstest::rstest;

use crate::Error;
use crate::tiling::tiles::Connectors;
use collections::HashSet;
use crate::tiling::slot_grid::*;
use crate::tests::fixtures::{
    rand_bool,
    rand_float,
    rand_string,
    rand_usize,
    random_missing_tile_policy,
    random_tile_type,
    random_vacancy_policy,
    RANDOM_STRINGS,
    RANDOM_USIZE,
    RANDOM_USIZES_SMALL,
};
use crate::tiling::tiles::{Tile, TileContent};

// noinspection DuplicatedCode
#[rstest]
#[rustfmt::skip]
// region one row, one col, three tiles, no border_tile_id, success
#[case::one_row_one_col_three_tiles_no_border_tile_id_success(
    1,
    1,
    TileList {
        tiles: vec![
            Tile {
                id: 0,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: rand_usize(1, 3),
                    east: rand_usize(1, 3),
                    south: rand_usize(1, 4),
                    west: rand_usize(1, 4),
                },
                weight: rand_float() + 5.3,
            },
            Tile {
                id: 1,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: rand_usize(1, 5),
                    east: rand_usize(1, 5),
                    south: rand_usize(1, 6),
                    west: rand_usize(1, 6),
                },
                weight: rand_float() + 10.2,
            },
            Tile {
                id: 2,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: rand_usize(1, 7),
                    east: rand_usize(1, 7),
                    south: rand_usize(1, 8),
                    west: rand_usize(1, 8),
                },
                weight: rand_float(),
            },
        ],
        horizontal_connector_names: vec![
            rand_string(rand_usize(0, 10)),
            rand_string(rand_usize(0, 10)),
            rand_string(rand_usize(0, 10)),
            rand_string(rand_usize(0, 10)),
            RANDOM_STRINGS[3].clone(),
        ],
        vertical_connector_names: vec![RANDOM_STRINGS[5].clone()],
        tile_type: random_tile_type(),
    },
    None,
    random_missing_tile_policy(),
    TileSelectionPolicy::Priority,
    random_vacancy_policy(),
    array![
        [
            None,
            None,
        ],
        [
            None,
            Some(GridSlot {
                tile_id: Some(1),
                direction: None,
                predecessor: None,
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
        ],
    ],
    Ok(()),
)]
// endregion
// region one row, one col, three tiles, no border_tile_id, alternative, success
#[case::one_row_one_col_three_tiles_no_border_tile_id_alternative_success(
    1,
    1,
    TileList {
        tiles: vec![
            Tile {
                id: 0,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: rand_usize(1, 3),
                    east: rand_usize(1, 3),
                    south: rand_usize(1, 4),
                    west: rand_usize(1, 4),
                },
                weight: rand_float() + 5.3,
            },
            Tile {
                id: 1,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: rand_usize(1, 7),
                    east: rand_usize(1, 7),
                    south: rand_usize(1, 8),
                    west: rand_usize(1, 8),
                },
                weight: rand_float(),
            },
            Tile {
                id: 2,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: rand_usize(1, 5),
                    east: rand_usize(1, 5),
                    south: rand_usize(1, 6),
                    west: rand_usize(1, 6),
                },
                weight: rand_float() * 10.2 + 6.4,
            },
        ],
        horizontal_connector_names: vec![
            rand_string(rand_usize(0, 10)),
            rand_string(rand_usize(0, 10)),
            rand_string(rand_usize(0, 10)),
            rand_string(rand_usize(0, 10)),
            RANDOM_STRINGS[3].clone(),
        ],
        vertical_connector_names: vec![RANDOM_STRINGS[5].clone()],
        tile_type: random_tile_type(),
    },
    None,
    random_missing_tile_policy(),
    TileSelectionPolicy::Priority,
    random_vacancy_policy(),
    array![
        [
            None,
            None,
        ],
        [
            None,
            Some(GridSlot {
                tile_id: Some(2),
                direction: None,
                predecessor: None,
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
        ],
    ],
    Ok(()),
)]
// endregion
// region one row, one col, three tiles, border_tile_id 1, success
#[case::one_row_one_col_three_tiles_border_tile_id_1_success(
    1,
    1,
    TileList {
        tiles: vec![
            Tile {
                id: 0,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: rand_usize(1, 3),
                    east: rand_usize(1, 3),
                    south: rand_usize(1, 4),
                    west: rand_usize(1, 4),
                },
                weight: rand_float() + 5.3,
            },
            Tile {
                id: 1,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: 1,
                    east: 4,
                    south: 3,
                    west: 3,
                },
                weight: rand_float() + 10.2,
            },
            Tile {
                id: 2,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: 3,
                    east: 3,
                    south: 1,
                    west: 4,
                },
                weight: rand_float(),
            },
        ],
        horizontal_connector_names: vec![
            rand_string(rand_usize(0, 10)),
            rand_string(rand_usize(0, 10)),
            rand_string(rand_usize(0, 10)),
            rand_string(rand_usize(0, 10)),
            RANDOM_STRINGS[3].clone(),
        ],
        vertical_connector_names: vec![RANDOM_STRINGS[5].clone()],
        tile_type: random_tile_type(),
    },
    Some(1),
    random_missing_tile_policy(),
    TileSelectionPolicy::Priority,
    random_vacancy_policy(),
    array![
        [
            None,
            None,
        ],
        [
            None,
            Some(GridSlot {
                tile_id: Some(2),
                direction: None,
                predecessor: None,
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
        ],
    ],
    Ok(()),
)]
// endregion
// region one row, one col, three tiles, border_tile_id 2, success
#[case::one_row_one_col_three_tiles_border_tile_id_2_success(
    1,
    1,
    TileList {
        tiles: vec![
            Tile {
                id: 0,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: rand_usize(1, 3),
                    east: rand_usize(1, 3),
                    south: rand_usize(1, 4),
                    west: rand_usize(1, 4),
                },
                weight: rand_float() + 5.3,
            },
            Tile {
                id: 1,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: 1,
                    east: 4,
                    south: 3,
                    west: 3,
                },
                weight: rand_float() + 10.2,
            },
            Tile {
                id: 2,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: 3,
                    east: 3,
                    south: 1,
                    west: 4,
                },
                weight: rand_float(),
            },
        ],
        horizontal_connector_names: vec![
            rand_string(rand_usize(0, 10)),
            rand_string(rand_usize(0, 10)),
            rand_string(rand_usize(0, 10)),
            rand_string(rand_usize(0, 10)),
            RANDOM_STRINGS[3].clone(),
        ],
        vertical_connector_names: vec![RANDOM_STRINGS[5].clone()],
        tile_type: random_tile_type(),
    },
    Some(2),
    random_missing_tile_policy(),
    TileSelectionPolicy::Priority,
    random_vacancy_policy(),
    array![
        [
            None,
            None,
        ],
        [
            None,
            Some(GridSlot {
                tile_id: Some(1),
                direction: None,
                predecessor: None,
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
        ],
    ],
    Ok(()),
)]
// endregion
// region two rows, three cols, four tiles, avoid vacancies, success
#[case::two_rows_three_cols_four_tiles_avoid_vacancies_success(
    2,
    3,
    TileList {
        tiles: vec![
            Tile {
                id: 0,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 3,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: *RANDOM_USIZE + 2,
                },
                weight: rand_float(),
            },
            Tile {
                id: 1,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 2,
                    east: *RANDOM_USIZE + 3,
                    south: *RANDOM_USIZE + 3,
                    west: *RANDOM_USIZE + 2,
                },
                weight: rand_float() + 5.3,
            },
            Tile {
                id: 2,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 2,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: *RANDOM_USIZE + 3,
                },
                weight: -1.0 * rand_float(),
            },
            Tile {
                id: 3,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 3,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: *RANDOM_USIZE + 3,
                },
                weight: -7.3 * rand_float() - 1.0,
            },
            Tile {
                id: 4,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: 0,
                    east: 0,
                    south: *RANDOM_USIZE + 2,
                    west: 0,
                },
                weight: -70.3 * rand_float() - 2.0,
            },
            Tile {
                id: 5,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 4,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: *RANDOM_USIZE + 2,
                },
                weight: rand_float() + 92.0,
            },
        ],
        horizontal_connector_names: RANDOM_STRINGS[..10].to_vec(),
        vertical_connector_names: RANDOM_STRINGS[10..].to_vec(),
        tile_type: random_tile_type(),
    },
    Some(4),
    random_missing_tile_policy(),
    TileSelectionPolicy::Priority,
    if rand_bool() {
        VacancyPolicy::Avoid
    } else {
        VacancyPolicy::AvoidStrict
    },
    array![
        [
            None,
            None,
            None,
            None,
        ],
        [
            None,
            Some(GridSlot {
                tile_id: Some(2),
                direction: None,
                predecessor: Some((2, 1)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
            Some(GridSlot {
                tile_id: Some(1),
                direction: None,
                predecessor: None,
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
            Some(GridSlot {
                tile_id: Some(2),
                direction: None,
                predecessor: Some((1, 2)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
        ],
        [
            None,
            Some(GridSlot {
                tile_id: Some(2),
                direction: None,
                predecessor: Some((2, 2)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::from([
                    Connectors {
                        north: *RANDOM_USIZE + 4,
                        east: *RANDOM_USIZE + 2,
                        south: *RANDOM_USIZE + 2,
                        west: *RANDOM_USIZE + 2,
                    },
                    Connectors {
                        north: *RANDOM_USIZE + 3,
                        east: *RANDOM_USIZE + 2,
                        south: *RANDOM_USIZE + 2,
                        west: *RANDOM_USIZE + 2,
                    },
                ]),
            }),
            Some(GridSlot {
                tile_id: Some(0),
                direction: None,
                predecessor: Some((2, 3)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
            Some(GridSlot {
                tile_id: Some(1),
                direction: None,
                predecessor: Some((1, 3)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
        ],
    ],
    Ok(()),
)]
// endregion
// region two rows, three cols, four tiles, ignore vacancies, error
#[case::two_rows_three_cols_four_tiles_ignore_vacancies_error(
    2,
    3,
    TileList {
        tiles: vec![
            Tile {
                id: 0,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 3,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: *RANDOM_USIZE + 2,
                },
                weight: rand_float(),
            },
            Tile {
                id: 1,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 2,
                    east: *RANDOM_USIZE + 3,
                    south: *RANDOM_USIZE + 3,
                    west: *RANDOM_USIZE + 2,
                },
                weight: rand_float() + 5.3,
            },
            Tile {
                id: 2,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 2,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: *RANDOM_USIZE + 3,
                },
                weight: -1.0 * rand_float(),
            },
            Tile {
                id: 3,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 3,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: *RANDOM_USIZE + 3,
                },
                weight: -7.3 * rand_float() - 1.0,
            },
            Tile {
                id: 4,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: 0,
                    east: 0,
                    south: *RANDOM_USIZE + 2,
                    west: 0,
                },
                weight: -70.3 * rand_float() - 2.0,
            },
            Tile {
                id: 5,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 4,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: *RANDOM_USIZE + 2,
                },
                weight: rand_float() + 92.0,
            },
        ],
        horizontal_connector_names: RANDOM_STRINGS[..10].to_vec(),
        vertical_connector_names: RANDOM_STRINGS[10..].to_vec(),
        tile_type: random_tile_type(),
    },
    Some(4),
    MissingTilePolicy::Exit,
    TileSelectionPolicy::Priority,
    VacancyPolicy::Ignore,
    array![
        [
            None,
            None,
            None,
            None,
        ],
        [
            None,
            Some(GridSlot {
                tile_id: None,
                direction: None,
                predecessor: Some((2, 1)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
            Some(GridSlot {
                tile_id: Some(1),
                direction: Some(Direction::East),
                predecessor: None,
                exposed_edges: vec![Direction::West],
                excluded_connectors: HashSet::new(),
            }),
            Some(GridSlot {
                tile_id: Some(2),
                direction: Some(Direction::South),
                predecessor: Some((1, 2)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
        ],
        [
            None,
            Some(GridSlot {
                tile_id: Some(5),
                direction: Some(Direction::North),
                predecessor: Some((2, 2)),
                exposed_edges: vec![Direction::North],
                excluded_connectors: HashSet::new(),
            }),
            Some(GridSlot {
                tile_id: Some(0),
                direction: Some(Direction::West),
                predecessor: Some((2, 3)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
            Some(GridSlot {
                tile_id: Some(1),
                direction: Some(Direction::West),
                predecessor: Some((1, 3)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
        ],
    ],
    Err(Error::TileNotFoundException(format!(
        "no tiles available with Connectors {{ north: “{}”, east: “{}”, south: “{}”, west: “{}” }}",
        RANDOM_STRINGS[*RANDOM_USIZE + 2 + 10],
        RANDOM_STRINGS[*RANDOM_USIZE + 2],
        RANDOM_STRINGS[*RANDOM_USIZE + 4 + 10],
        RANDOM_STRINGS[0],
    ))),
)]
// endregion
// region two rows, three cols, four tiles, avoid missing tile, success
#[case::two_rows_three_cols_four_tiles_avoid_missing_tile_success(
    2,
    3,
    TileList {
        tiles: vec![
            Tile {
                id: 0,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 3,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: *RANDOM_USIZE + 2,
                },
                weight: rand_float(),
            },
            Tile {
                id: 1,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 2,
                    east: *RANDOM_USIZE + 3,
                    south: *RANDOM_USIZE + 3,
                    west: *RANDOM_USIZE + 2,
                },
                weight: rand_float() + 5.3,
            },
            Tile {
                id: 2,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 2,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: *RANDOM_USIZE + 3,
                },
                weight: -1.0 * rand_float(),
            },
            Tile {
                id: 3,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 3,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: *RANDOM_USIZE + 3,
                },
                weight: -7.3 * rand_float() - 1.0,
            },
            Tile {
                id: 4,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: 0,
                    east: 0,
                    south: *RANDOM_USIZE + 2,
                    west: 0,
                },
                weight: -70.3 * rand_float() - 2.0,
            },
            Tile {
                id: 5,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 4,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: *RANDOM_USIZE + 2,
                },
                weight: rand_float() + 92.0,
            },
        ],
        horizontal_connector_names: RANDOM_STRINGS[..10].to_vec(),
        vertical_connector_names: RANDOM_STRINGS[10..].to_vec(),
        tile_type: random_tile_type(),
    },
    Some(4),
    MissingTilePolicy::Avoid,
    TileSelectionPolicy::Priority,
    VacancyPolicy::Ignore,
    array![
        [
            None,
            None,
            None,
            None,
        ],
        [
            None,
            Some(GridSlot {
                tile_id: Some(2),
                direction: None,
                predecessor: Some((2, 1)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
            Some(GridSlot {
                tile_id: Some(1),
                direction: None,
                predecessor: None,
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
            Some(GridSlot {
                tile_id: Some(2),
                direction: None,
                predecessor: Some((1, 2)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
        ],
        [
            None,
            Some(GridSlot {
                tile_id: Some(2),
                direction: None,
                predecessor: Some((2, 2)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::from([
                    Connectors {
                        north: *RANDOM_USIZE + 4,
                        east: *RANDOM_USIZE + 2,
                        south: *RANDOM_USIZE + 2,
                        west: *RANDOM_USIZE + 2,
                    },
                    Connectors {
                        north: *RANDOM_USIZE + 3,
                        east: *RANDOM_USIZE + 2,
                        south: *RANDOM_USIZE + 2,
                        west: *RANDOM_USIZE + 2,
                    },
                ]),
            }),
            Some(GridSlot {
                tile_id: Some(0),
                direction: None,
                predecessor: Some((2, 3)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
            Some(GridSlot {
                tile_id: Some(1),
                direction: None,
                predecessor: Some((1, 3)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
        ],
    ],
    Ok(()),
)]
// endregion
// region two rows, three cols, four tiles, avoid vacancies, alternative, success
#[case::two_rows_three_cols_four_tiles_avoid_vacancies_alternative_success(
    2,
    3,
    TileList {
        tiles: vec![
            Tile {
                id: 0,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 3,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: *RANDOM_USIZE + 2,
                },
                weight: rand_float(),
            },
            Tile {
                id: 1,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 2,
                    east: *RANDOM_USIZE + 3,
                    south: *RANDOM_USIZE + 3,
                    west: *RANDOM_USIZE + 2,
                },
                weight: rand_float() + 5.3,
            },
            Tile {
                id: 2,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 2,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: *RANDOM_USIZE + 3,
                },
                weight: -1.0 * rand_float(),
            },
            Tile {
                id: 3,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 3,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: *RANDOM_USIZE + 3,
                },
                weight: -7.3 * rand_float() - 1.0,
            },
            Tile {
                id: 4,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: 0,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: 0,
                },
                weight: -70.3 * rand_float() - 2.0,
            },
            Tile {
                id: 5,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: *RANDOM_USIZE + 4,
                    east: *RANDOM_USIZE + 2,
                    south: *RANDOM_USIZE + 2,
                    west: *RANDOM_USIZE + 2,
                },
                weight: rand_float() + 92.0,
            },
        ],
        horizontal_connector_names: RANDOM_STRINGS[..10].to_vec(),
        vertical_connector_names: RANDOM_STRINGS[10..].to_vec(),
        tile_type: random_tile_type(),
    },
    Some(4),
    MissingTilePolicy::Avoid,
    TileSelectionPolicy::Priority,
    if rand_bool() {
        VacancyPolicy::Avoid
    } else {
        VacancyPolicy::AvoidStrict
    },
    array![
        [
            None,
            None,
            None,
            None,
        ],
        [
            None,
            Some(GridSlot {
                tile_id: Some(1),
                direction: None,
                predecessor: Some((2, 1)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
            Some(GridSlot {
                tile_id: Some(2),
                direction: None,
                predecessor: None,
                exposed_edges: vec![],
                excluded_connectors: HashSet::from([
                    Connectors {
                        north: *RANDOM_USIZE + 2,
                        east: *RANDOM_USIZE + 3,
                        south: *RANDOM_USIZE + 3,
                        west: *RANDOM_USIZE + 2,
                    },
                ]),
            }),
            Some(GridSlot {
                tile_id: Some(1),
                direction: None,
                predecessor: Some((1, 2)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
        ],
        [
            None,
            Some(GridSlot {
                tile_id: Some(0),
                direction: None,
                predecessor: Some((2, 2)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::from([
                    Connectors {
                        north: *RANDOM_USIZE + 4,
                        east: *RANDOM_USIZE + 2,
                        south: *RANDOM_USIZE + 2,
                        west: *RANDOM_USIZE + 2,
                    },
                ]),
            }),
            Some(GridSlot {
                tile_id: Some(1),
                direction: None,
                predecessor: Some((2, 3)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::new(),
            }),
            Some(GridSlot {
                tile_id: Some(3),
                direction: None,
                predecessor: Some((1, 3)),
                exposed_edges: vec![],
                excluded_connectors: HashSet::from([
                    Connectors {
                        north: *RANDOM_USIZE + 3,
                        east: *RANDOM_USIZE + 2,
                        south: *RANDOM_USIZE + 2,
                        west: *RANDOM_USIZE + 2,
                    },
                ]),
            }),
        ],
    ],
    Ok(()),
)]
// endregion
// region two rows, one col, three tiles, no border_tile_id, success
#[case::two_rows_one_col_three_tiles_no_border_tile_id_success(
    5,
    1,
    TileList {
        tiles: vec![
            Tile {
                id: 0,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: rand_usize(1, 3),
                    east: rand_usize(6, 8),
                    south: rand_usize(1, 4),
                    west: rand_usize(1, 4),
                },
                weight: rand_float() + 5.3,
            },
            Tile {
                id: 1,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: 1,
                    east: 1,
                    south: 1,
                    west: 1,
                },
                weight: rand_float() + 10.2,
            },
            Tile {
                id: 2,
                name: rand_string(rand_usize(0, 2)),
                content: TileContent::Empty,
                connectors: Connectors {
                    north: RANDOM_USIZES_SMALL[3],
                    east: 1,
                    south: RANDOM_USIZES_SMALL[3],
                    west: 1,
                },
                weight: rand_float() + 7.,
            },
        ],
        horizontal_connector_names: vec![
            rand_string(rand_usize(0, 10)),
            rand_string(rand_usize(0, 10)),
            rand_string(rand_usize(0, 10)),
            rand_string(rand_usize(0, 10)),
            RANDOM_STRINGS[3].clone(),
        ],
        vertical_connector_names: RANDOM_STRINGS.clone(),
        tile_type: random_tile_type(),
    },
    Some(1),
    MissingTilePolicy::Avoid,
    TileSelectionPolicy::Priority,
    if rand_bool() {
        VacancyPolicy::Avoid
    } else {
        VacancyPolicy::AvoidStrict
    },
    array![
        [None, None],
        [None, None],
        [None, None],
        [
            None,
            Some(GridSlot {
                tile_id: None,
                direction: None,
                predecessor: None,
                exposed_edges: vec![],
                excluded_connectors: HashSet::from([
                    Connectors {
                        north: RANDOM_USIZES_SMALL[3],
                        east: 1,
                        south: RANDOM_USIZES_SMALL[3],
                        west: 1,
                    },
                ]),
            }),
        ],
        [None, None],
        [None, None],
    ],
    Err(Error::TileNotFoundException(
        "no more tiles left to try, backtracking failed.".to_string()
    )),
)]
// endregion
fn test_populate(
    #[case] num_rows: usize,
    #[case] num_cols: usize,
    #[case] tile_list: TileList,
    #[case] border_tile_id: Option<usize>,
    #[case] missing_tile_policy: MissingTilePolicy,
    #[case] tile_selection_policy: TileSelectionPolicy,
    #[case] vacancy_policy: VacancyPolicy,
    #[case] expected_slot_grid: Array2<Option<GridSlot>>,
    #[case] expected_result: Result<(), Error>,
) {
    let mut slot_grid = SlotGrid::new(
        num_rows,
        num_cols,
        tile_list,
        border_tile_id,
        missing_tile_policy,
        tile_selection_policy,
        vacancy_policy,
    )
    .expect("the number of rows, columns as well as the border tile ID should be valid");
    let result = slot_grid.populate();

    assert_eq!(result, expected_result);
    assert_eq!(slot_grid.grid, expected_slot_grid);
    // Access slot_grid to ensure it was not moved.
    drop(slot_grid);
}
